﻿using SQLBuilder.Extensions;
using System;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace K3Cloud.WebApi.Core.IoC.Extensions
{
    /// <summary>
    /// 逻辑
    /// </summary>
    public enum EnumAndOr : int
    {
        /// <summary>
        /// 并且
        /// </summary>
        And,
        /// <summary>
        /// 或者
        /// </summary>
        Or
    }
    public static class LinqExtensions
    {
        /// <summary>
        /// 增加筛选条件到表达式树
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="thisExpression">基树</param>
        /// <param name="fieldName">字段</param>
        /// <param name="optionString">比较</param>
        /// <param name="value">值</param>
        /// <param name="expressionType">逻辑</param>
        /// <returns></returns>
        public static Expression<Func<T, bool>> AddFilter<T>(this Expression<Func<T, bool>>? thisExpression, string? fieldName, string? optionString, object? value, EnumAndOr expressionType = default)
        {
            Type t = typeof(T);
            PropertyInfo propertyAny = t.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(x => x.Name.Equals(fieldName, StringComparison.OrdinalIgnoreCase)).FirstOrDefault() ?? throw new Exception($"{t.FullName}中不存在{fieldName}属性,请检查!");
            ParameterExpression parameterExpression = Expression.Parameter(t);
            PropertyInfo property = propertyAny;
            Type propertyType = property.PropertyType;
            object? propertyValue = propertyType.ParseNullableToType() == typeof(int) ||
                propertyType.ParseNullableToType() == typeof(short) ||
                propertyType.ParseNullableToType() == typeof(long) ||
                propertyType.ParseNullableToType() == typeof(float) ||
                propertyType.ParseNullableToType() == typeof(decimal) ||
                propertyType.ParseNullableToType() == typeof(double)
                ? value != null ? Convert.ChangeType(value, propertyType.ParseNullableToType()) : 0
                : value != null ? Convert.ChangeType(value, propertyType.ParseNullableToType()) : null;
            // 这里还有哪些数字类型默认值是0的没有考虑到 

            MemberExpression expressionProperty = Expression.PropertyOrField(parameterExpression, property.Name);
            ConstantExpression expressionValue = Expression.Constant(propertyValue, propertyType);
            Expression[] paramsValue = [expressionValue];
            Type[] paramsType = [propertyType];
            MethodInfo optionField_Contains = propertyType.GetMethod("Contains", paramsType);
            MethodInfo optionField_IsNullOrEmpty = propertyType.GetMethod("IsNullOrEmpty", paramsType);
            MethodInfo optionField_StartsWith = propertyType.GetMethod("StartsWith", paramsType);
            MethodInfo optionField_EndsWith = propertyType.GetMethod("EndsWith", paramsType);

            object? optionExpression = null;
            switch (optionString)
            {
                case "=":
                case "equal":
                    optionExpression = Expression.Equal(expressionProperty, expressionValue);
                    break;
                case "!=":
                case "<>":
                case "unequal":
                    optionExpression = Expression.NotEqual(expressionProperty, expressionValue);
                    break;
                case ">":
                case "greater_than":
                    optionExpression = Expression.GreaterThan(expressionProperty, expressionValue);
                    break;
                case ">=":
                    optionExpression = Expression.GreaterThanOrEqual(expressionProperty, expressionValue);
                    break;
                case "<":
                case "less_than":
                    optionExpression = Expression.LessThan(expressionProperty, expressionValue);
                    break;
                case "<=":
                    optionExpression = Expression.LessThanOrEqual(expressionProperty, expressionValue);
                    break;
                case "like":
                    if (optionField_Contains != null)
                    {
                        optionExpression = Expression.Call(expressionProperty, optionField_Contains, paramsValue);
                    }
                    break;
                case "notlike":
                    if (optionField_Contains != null)
                    {
                        optionExpression = Expression.Not(Expression.Call(expressionProperty, optionField_Contains, paramsValue));
                    }
                    break;
                case "llike":
                case "start":
                    if (optionField_StartsWith != null)
                    {
                        optionExpression = Expression.Call(expressionProperty, optionField_StartsWith, paramsValue);
                    }
                    break;
                case "rlike":
                case "end":
                    if (optionField_EndsWith != null)
                    {
                        optionExpression = Expression.Call(expressionProperty, optionField_EndsWith, paramsValue);
                    }
                    break;
                case "empty":
                    if (optionField_IsNullOrEmpty != null)
                    {
                        optionExpression = Expression.Call(null, optionField_IsNullOrEmpty, expressionProperty);
                    }
                    break;
                case "notempty":
                    if (optionField_IsNullOrEmpty != null)
                    {
                        optionExpression = Expression.Not(Expression.Call(null, optionField_IsNullOrEmpty, expressionProperty));
                    }
                    break;
                default:
                    break;
            }
            Expression<Func<T, bool>> expression = Expression.Lambda<Func<T, bool>>((Expression?)optionExpression, [parameterExpression]);
            return thisExpression == null
                ? expression
                : expressionType == EnumAndOr.And ? thisExpression.And(expression) : thisExpression.Or(expression);
        }

        /// <summary>
        /// 添加And条件
        /// <![CDATA[
        /// 例：Expression<Func<PRD_INSTOCK, bool>> t = default;
        /// t = t.AndFilter(x => x.FBillNo != null);
        /// ]]>
        /// </summary>
        [DebuggerStepThrough]
        public static Expression<Func<T, bool>> AndFilter<T>(this Expression<Func<T, bool>>? first, Expression<Func<T, bool>> second)
        {
            return first == null ? second : first.And(second);
        }

        /// <summary>
        /// 添加Or条件
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="first"></param>
        /// <param name="second"></param>
        /// <returns></returns>
        [DebuggerStepThrough]
        public static Expression<Func<T, bool>> OrFilter<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
        {
            return first == null ? second : first.Or(second);
        }

    }
}
